package com.lal.springboot.controller;

import com.lal.sdk.util.IpUtil;
import com.lal.springboot.service.LalServerService;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/stream")
public class StreamProxyController {

    @Autowired
    private LalServerService lalServerService;

    /* ========== FLV / TS / fMP4 字节流直转 ========== */
    @GetMapping(value = "/flv/{streamName}", produces = "application/octet-stream")
    public void proxyFlvTs(@PathVariable String streamName,
                           @RequestParam String token,
                           HttpServletRequest req,
                           HttpServletResponse rsp) throws IOException {

        String clientIp = IpUtil.getRequestIp(req);
        if (!lalServerService.validateStreamAccess(token, streamName, clientIp)) {
            rsp.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid stream access token");
            return;
        }

        String target = lalServerService.getOriginalStreamUrl(streamName, "flv");
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) new URL(target).openConnection();
            conn.setConnectTimeout(3000);
            conn.setReadTimeout(0);
            conn.setRequestMethod("GET");
            copyHeaders(req, conn);

            int responseCode = conn.getResponseCode();
            rsp.setStatus(responseCode);

            // 复制所有重要的响应头
            copyResponseHeaders(conn, rsp);
            rsp.setHeader("Access-Control-Allow-Origin", "*");
            rsp.setHeader("Connection", "close");
            rsp.setHeader("Cache-Control", "no-cache");
            rsp.setHeader("Pragma", "no-cache");

            if (responseCode == HttpURLConnection.HTTP_OK) {
                rsp.setBufferSize(8192);
                try (InputStream input = conn.getInputStream();
                     OutputStream output = rsp.getOutputStream()) {
                    IOUtils.copy(input, output);
                }
            }
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    /* ========== HLS : m3u8 / ts ========== */
    @GetMapping(value = {"/hls/{streamName}/{token}/playlist.m3u8", "/hls/{streamName}/{token}/**"})
    public void proxyHls(@PathVariable String streamName,
                         @PathVariable String token,
                         HttpServletRequest req,
                         HttpServletResponse rsp) throws IOException {

        String clientIp = IpUtil.getRequestIp(req);
        if (!lalServerService.validateStreamAccess(token, streamName, clientIp)) {
            rsp.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid stream access token");
            return;
        }

        // 获取请求的完整路径
        String requestPath = req.getRequestURI();

        // 提取streamName之后的部分
        String relativePath = requestPath.substring(requestPath.indexOf(streamName+"/"+token) + streamName.length());
        if (relativePath.isEmpty()) {
            relativePath = "/";
        }
        String baseM3u8Url = lalServerService.getOriginalStreamUrl(streamName, "hls");

        // 构建完整的目标URL
        String targetUrl;
        if (requestPath.contains(".m3u8")) {
            // 请求m3u8主文件
            targetUrl = baseM3u8Url;
        } else {
            // 请求ts文件或其他资源
            // 从baseM3u8Url提取基础目录
            int lastSlashIndex = baseM3u8Url.lastIndexOf('/');
            String baseDir = baseM3u8Url.substring(0, lastSlashIndex + 1);
            targetUrl = baseDir + relativePath.substring(1).replace(token,"").substring(1); // 去掉开头的斜杠
        }
        proxyToUpstream(targetUrl, req, rsp, streamName, token);
    }

    /* ========== WebRTC : 仅转发信令（WHIP/WHEP） ========== */
    @RequestMapping(value = "/webrtc/{streamName}",
            method = {RequestMethod.POST, RequestMethod.PATCH, RequestMethod.DELETE})
    public ResponseEntity<byte[]> proxyWebrtc(@PathVariable String streamName,
                                              @RequestParam String token,
                                              @RequestBody(required = false) byte[] body,
                                              HttpServletRequest req) throws IOException {

        String clientIp = IpUtil.getRequestIp(req);
        if (!lalServerService.validateStreamAccess(token, streamName, clientIp)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Invalid stream access token".getBytes());
        }

        String target = lalServerService.getOriginalStreamUrl(streamName, "webrtc");
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) new URL(target).openConnection();
            conn.setRequestMethod(req.getMethod());
            conn.setDoOutput(true);
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(10000);
            copyHeaders(req, conn);

            if (body != null && body.length > 0) {
                try (OutputStream os = conn.getOutputStream()) {
                    os.write(body);
                }
            }

            int status = conn.getResponseCode();
            byte[] resp = IOUtils.toByteArray(
                    status < 400 ? conn.getInputStream() : conn.getErrorStream());

            // 复制响应头
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.parseMediaType(conn.getContentType()));
            headers.setAccessControlAllowOrigin("*");

            return ResponseEntity.status(status)
                    .headers(headers)
                    .body(resp);
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    private void proxyToUpstream(String targetUrl, HttpServletRequest req, HttpServletResponse rsp,
                                 String streamName, String token) {
        HttpURLConnection conn = null;
        try {
            URL url = new URL(targetUrl);
            conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(3000);
            conn.setReadTimeout(10000);
            conn.setRequestMethod("GET");

            int responseCode = conn.getResponseCode();
            rsp.setStatus(responseCode);

            // 设置正确的Content-Type
            if (targetUrl.endsWith(".m3u8")) {
                rsp.setContentType("application/vnd.apple.mpegurl");
                rsp.setCharacterEncoding("UTF-8");
            } else if (targetUrl.endsWith(".ts")) {
                rsp.setContentType("video/MP2T");
            }

            rsp.setHeader("Access-Control-Allow-Origin", "*");
            rsp.setHeader("Cache-Control", "no-cache");

            if (responseCode == HttpURLConnection.HTTP_OK) {
                // 如果是m3u8文件，需要解析并重写ts路径
                if (targetUrl.endsWith(".m3u8")) {
                    rewriteM3u8Content(conn, rsp, streamName, token);
                } else {
                    // 直接复制ts文件内容
                    try (InputStream input = conn.getInputStream();
                         OutputStream output = rsp.getOutputStream()) {
                        IOUtils.copy(input, output);
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("Proxy error for URL: "+targetUrl+e);
            if (!rsp.isCommitted()) {
                try {
                    rsp.sendError(HttpServletResponse.SC_BAD_GATEWAY, "Upstream error");
                } catch (IOException ex) {
                    System.out.println("Failed to send error response"+ex);
                }
            }
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    private void rewriteM3u8Content(HttpURLConnection conn, HttpServletResponse rsp,
                                    String streamName, String token) throws IOException {
        StringBuilder content = new StringBuilder();
        try (InputStream input = conn.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                // 重写ts文件行
                if (line.trim().endsWith(".ts") && !line.trim().startsWith("#")) {
                    // 这是ts文件行，重写为代理路径
                    String tsFileName = line.trim();
                    String proxyPath = tsFileName;
                    content.append(proxyPath).append("\n");
                } else {
                    // 保持其他行不变
                    content.append(line).append("\n");
                }
            }
        }

        try (OutputStream output = rsp.getOutputStream()) {
            output.write(content.toString().getBytes(StandardCharsets.UTF_8));
        }
    }

    private void copyHeaders(HttpServletRequest req, HttpURLConnection conn) {
        Enumeration<String> names = req.getHeaderNames();
        while (names.hasMoreElements()) {
            String name = names.nextElement();
            if ("host".equalsIgnoreCase(name) || "content-length".equalsIgnoreCase(name)) {
                continue;
            }
            conn.setRequestProperty(name, req.getHeader(name));
        }
    }

    private void copyResponseHeaders(HttpURLConnection conn, HttpServletResponse rsp) {
        // 复制内容类型
        String contentType = conn.getContentType();
        if (contentType != null) {
            rsp.setContentType(contentType);
        }

        // 复制其他重要头信息
        Map<String, List<String>> headerFields = conn.getHeaderFields();
        for (Map.Entry<String, List<String>> entry : headerFields.entrySet()) {
            String key = entry.getKey();
            if (key != null && !key.equalsIgnoreCase("Transfer-Encoding")) {
                for (String value : entry.getValue()) {
                    rsp.addHeader(key, value);
                }
            }
        }
    }
}